/*******************************************************************************
 * Copyright (c) 2012 Alexandr Tsvetkov.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Alexandr Tsvetkov - initial API and implementation
 * 
 * Project:
 *     TAO Library
 * 
 * File name:
 *     NetCheck.java
 *     
 * License agreement:
 *
 * 1. This code is published AS IS. Author is not responsible for any damage that can be
 *    caused by any application that uses this code.
 * 2. Author does not give a garantee, that this code is error free.
 * 3. This code can be used in NON-COMMERCIAL applications AS IS without any special
 *    permission from author.
 * 4. This code can be modified without any special permission from author IF AND ONLY IF
 *    this license agreement will remain unchanged.
 * 5. SPECIAL PERMISSION for this code usage in COMMERCIAL application SHOULD be obtained
 *    from author.
 ******************************************************************************/
package ua.at.tsvetkov.net;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;

import ua.at.tsvetkov.strings.Const;
import ua.at.tsvetkov.util.Log;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;

/**
 * Класс для постоянной проверки связи с сайтом. Запускается в потоке.
 * @author A.Tsvetkov 2011 http://tsvetkov.at.ua mailto:al@ukr.net
 */
public class NetCheck implements Runnable {

	/**
	 * Таймаут по умолчанию в миллисекундах.
	 */
	public static final int		TIMEOUT_DEFAULT			= 2000;
	/**
	 * Время на паузу в сканировании по умолчанию в миллисекундах.
	 */
	public static final int		SCANNING_TIME_DEFAULT	= 5000;

	private static NetCheck		instance;

	private Context				context;
	private long					checkTime;
	private URL						url;
	private HttpURLConnection	urlc;
	private NetStatus				netStatus, netOldStatus;
	private boolean				mustStop;
	private int						timeout;
	private NetStatusListener	netListener;
	private boolean				isRunning;
	private long					startTime;

	/**
	 * Конструктор постоянно проверяющий наличие интернета. Запускать в потоке.
	 *
	 * @param context контекст.
	 * @param urlStr адрес сайта с которым производятся проверочные соединения.
	 * @param checkTime интервал сканирования в миллисекундах.
	 * @param timeout таймаут в миллисекундах.
	 */
	public NetCheck(Context context, String urlStr, long checkTime, int timeout) {
		init(context, urlStr, checkTime, timeout);
	}

	/**
	 * Check internet access.
	 * 
	 * @param context 
	 * @param urlStr testing site.
	 */
	public NetCheck(Context context, String urlStr) {
		init(context, urlStr, SCANNING_TIME_DEFAULT, TIMEOUT_DEFAULT);
	}

	/**
	 * Создает статичный экземпляр класса если экземпляр не инициализирован.
	 *
	 * @param context контекст.
	 * @param urlStr адрес сайта с которым производятся проверочные соединения.
	 * @param checkTime интервал сканирования в миллисекундах.
	 * @param timeout таймаут в миллисекундах.
	 * @return
	 */
	public static NetCheck newInstance(Context context, String urlStr, long checkTime, int timeout) {
		if (instance == null) {
			instance = new NetCheck(context, urlStr, checkTime, timeout);
		} else {
			instance.setDefaultSite(urlStr);
			instance.checkTime = checkTime;
			instance.timeout = timeout;
		}
		return instance;
	}

	/**
	 * Создает статичный экземпляр класса если экземпляр не инициализирован.
	 *
	 * @param context контекст.
	 * @param urlStr адрес сайта с которым производятся проверочные соединения.
	 * @return
	 */
	public static NetCheck newInstance(Context context, String urlStr) {
		if (instance == null) {
			instance = new NetCheck(context, urlStr);
		} else {
			instance.setDefaultSite(urlStr);
		}
		return instance;
	}

	/**
	 * Возвращает статичный экземпляр класса.
	 *
	 * @param context контекст.
	 * @return
	 */
	public static NetCheck getInstance() {
		if (instance == null) {
			Log.w("Instance is not init!");
		}
		return instance;
	}

	/**
	 * Init data for test connection
	 *
	 * @param context
	 * @param urlStr
	 * @param checkTime
	 * @param timeout
	 */
	private void init(Context context, String urlStr, long checkTime, int timeout) {
		setDefaultSite(urlStr);
		this.context = context;
		this.checkTime = checkTime;
		this.timeout = timeout;
		this.isRunning = false;
	}

	/**
	 * Turn on WiFi 
	 *
	 * @param context
	 */
	public void turnOnWiFi(Context context) {
		WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
		WifiInfo info = wifi.getConnectionInfo();
		Log.v("WiFi Status: " + info.toString());
		if (wifi.getWifiState() != WifiManager.WIFI_STATE_ENABLED) {
			wifi.setWifiEnabled(true);
			Log.v("WiFi New Status: " + info.toString());
		}
	}

	/**
	 * Set scanning time
	 *
	 * @param checkTime time in ms
	 */
	public void setScnningTime(long checkTime) {
		this.checkTime = checkTime;
	}

	/**
	 * Set defoult site for test connection
	 *
	 * @param url адрес сайта
	 * @throws MalformedURLException
	 */
	public void setDefaultSite(String urlStr) {
		try {
			this.url = new URL(urlStr);
		} catch (MalformedURLException e) {
			Log.e("Bad URL " + urlStr, e);
		}
	}

	/**
	 * Set timeout
	 *
	 * @param timeout time in ms
	 */
	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}

	/**
	 * Назначает слушатель при изменении статуса интернета
	 *
	 * @param netListener
	 */
	public void setListener(NetStatusListener netListener) {
		this.netListener = netListener;
	}

	/**
	 * Проверяет наличие интернета
	 *
	 * @return
	 */
	public boolean isOnline() {
		while (netStatus == NetStatus.NOT_DEFINED_YET) { // Пауза до момента первой проверки
			try {
				Thread.sleep(100);
				if (System.currentTimeMillis() > startTime + TIMEOUT_DEFAULT) {
					netStatus = NetStatus.NO_NET;
					break;
				}
			} catch (InterruptedException e) {
				// nothing to do
			}
		}
		return this.netStatus == NetStatus.NET_OK;
	}

	/**
	 * Возвращает статус
	 *
	 * @return
	 */
	public NetStatus getStatus() {
		return this.netStatus;
	}

	/**
	 * Возвращает статус
	 *
	 * @return
	 */
	public String getStatusString() {
		switch (netStatus) {
			case NET_OK: {
				return Const.NET_OK;
			}
			case NO_NET: {
				return Const.NO_NET;
			}
			case FAULTY_URL: {
				return Const.FAULTY_URL;
			}
			case CONNECTION_MISSING: {
				return Const.CONN_MISSING;
			}
			case NOT_DEFINED_YET:
				break;
		}
		return "";
	}

	/**
	 * Возвращает статус
	 *
	 * @return
	 */
	public String getStatusStringRu() {
		switch (netStatus) {
			case NET_OK: {
				return Const.NET_OK_RU;
			}
			case NO_NET: {
				return Const.NO_NET_RU;
			}
			case FAULTY_URL: {
				return Const.FAULTY_URL_RU;
			}
			case CONNECTION_MISSING: {
				return Const.CONN_MISSING_RU;
			}
			case NOT_DEFINED_YET:
				break;
		}
		return "";
	}

	/**
	 * Проверяет запущена ли проверка
	 *
	 * @return
	 */
	public boolean isRunning() {
		return isRunning;
	}

	/**
	 * Останавливает проверку наличия интернета и завершает работу класса.
	 */
	public void stop() {
		mustStop = true;
	}

	@Override
	public void run() {
		this.netStatus = NetStatus.NOT_DEFINED_YET;
		this.startTime = System.currentTimeMillis();
		this.netOldStatus = this.netStatus;
		this.mustStop = false;
		this.isRunning = true;
		Log.v("Checking connection to " + url + " is starting");
		while (!mustStop) {
			try {
				if (isNetPresent(context) == false) {
					netStatus = NetStatus.CONNECTION_MISSING;
				}
				urlc = (HttpURLConnection) url.openConnection();
				urlc.setConnectTimeout(timeout);
				urlc.connect();
				if (urlc.getResponseCode() == 200) {
					netStatus = NetStatus.NET_OK;
				}
				urlc.disconnect();
				Thread.sleep(checkTime);
			} catch (SocketTimeoutException e1) {
				netStatus = NetStatus.NO_NET;
				Log.d("Network status " + netStatus.name());
			} catch (MalformedURLException e1) {
				netStatus = NetStatus.FAULTY_URL;
			} catch (IOException e) {
				netStatus = NetStatus.NO_NET;
			} catch (InterruptedException e) {
				// nothing
			} catch (NullPointerException e) {
				netStatus = NetStatus.NO_NET;
				Log.e("Connection=" + urlc);
			}
			if (netOldStatus != netStatus) {
				netListener.onChengeNetStatus(this, netStatus);
				netOldStatus = netStatus;
				Log.d("Network status changed to " + netStatus.name());
			}
		}
		this.isRunning = false;
	}

	/**
	 * Проверяет наличие сетевого подключения
	 *
	 * @param context
	 * @return
	 */
	private boolean isNetPresent(Context context) {
		if (((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo() == null) {
			return false;
		} else {
			return true;
		}
	}

}
